using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;

namespace Columbus
{
    [DataContract]
    internal class Journal
    {
        private ITransientStore transientStore;
        internal const string JournalKeyPattern = "NavigationJournalFor{0}.dat";
        internal const string JournalEntryKeyPattern = "NavigationJournalEntry{0}.dat";

        [DataMember]
        internal string NavigationFrameName { get; set; }

        private List<JournalEntry> historyRequests = new List<JournalEntry>();

        [DataMember]
        internal JournalEntry[] HistoryRequests
        {
            get { return historyRequests.ToArray(); }
            set
            {
                if (value == null)
                {
                    historyRequests.Clear();
                    return;
                }

                if (historyRequests == null)
                    historyRequests = new List<JournalEntry>();

                historyRequests.AddRange(value);
            }
        }

        internal Journal(string navigationFrameName)
        {
            NavigationFrameName = navigationFrameName;
        }

        internal Journal Initialize(ITransientStore persistentTransientStore)
        {
            transientStore = persistentTransientStore;
            return this;
        }

        internal NavigationRequest PopEntry()
        {
            var lastElement = historyRequests.LastOrDefault();
            if (lastElement != null)
                historyRequests.RemoveAt(historyRequests.Count - 1);
            else return null;

            var knownTypes = lastElement.KnownTypeNames.Select(Type.GetType).Where(t => t != null).ToArray();
            return transientStore.Load<NavigationRequest>(
                lastElement.EntryFileName,
                knownTypes);
        }

        internal void PushEntry(NavigationRequest request)
        {
            var entryFileName = String.Format(JournalEntryKeyPattern, Guid.NewGuid());
            var knownTypes = request.Parameters.Where(p => p.Value != null).Select(p => p.Value.GetType()).ToArray();

            try
            {
                transientStore.Save(
                    request,
                    entryFileName,
                    knownTypes);
            }
            catch (Exception ex)
            {
                throw new NavigationRequestSerializationException(ex);
            }

            historyRequests.Add(new JournalEntry(entryFileName, knownTypes.Select(t => t.AssemblyQualifiedName).ToArray()));
        }

        internal void Persist()
        {
            transientStore.Save(this, String.Format(JournalKeyPattern, NavigationFrameName));
        }
    }
}